1   /*                        __    __  __  __    __  ___
2    *                       \  \  /  /    \  \  /  /  __/
3    *                        \  \/  /  /\  \  \/  /  /
4    *                         \____/__/  \__\____/__/.ɪᴏ
5    * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ
6    */
7   package io.vavr.collection;
8   
9   import io.vavr.Tuple;
10  import io.vavr.Tuple2;
11  import io.vavr.control.Option;
12  import org.assertj.core.api.IterableAssert;
13  import org.junit.Test;
14  import org.junit.runner.RunWith;
15  import org.junit.runners.Parameterized;
16  
17  import java.util.*;
18  import java.util.function.BiConsumer;
19  import java.util.function.BinaryOperator;
20  import java.util.function.Function;
21  import java.util.function.Supplier;
22  import java.util.regex.Pattern;
23  import java.util.stream.Collector;
24  
25  import static java.util.Arrays.asList;
26  
27  @RunWith(Parameterized.class)
28  public abstract class AbstractMultimapTest extends AbstractTraversableTest {
29  
30      @Parameterized.Parameters
31      public static Collection<Object[]> data() {
32          return asList(new Object[][] {
33                  { Multimap.ContainerType.SEQ },
34                  { Multimap.ContainerType.SET },
35                  { Multimap.ContainerType.SORTED_SET }
36          });
37      }
38  
39      @Parameterized.Parameter
40      public Multimap.ContainerType containerType;
41  
42      @Override
43      protected <T> IterableAssert<T> assertThat(Iterable<T> actual) {
44          return new IterableAssert<T>(actual) {
45              @Override
46              public IterableAssert<T> isEqualTo(Object obj) {
47                  @SuppressWarnings("unchecked")
48                  final Iterable<T> expected = (Iterable<T>) obj;
49                  final java.util.Map<T, Integer> actualMap = countMap(actual);
50                  final java.util.Map<T, Integer> expectedMap = countMap(expected);
51                  assertThat(actualMap.size()).isEqualTo(expectedMap.size());
52                  actualMap.forEach((k, v) -> assertThat(v).isEqualTo(expectedMap.get(k)));
53                  return this;
54              }
55  
56              private java.util.Map<T, Integer> countMap(Iterable<? extends T> it) {
57                  final java.util.HashMap<T, Integer> cnt = new java.util.HashMap<>();
58                  it.forEach(i -> cnt.merge(i, 1, (v1, v2) -> v1 + v2));
59                  return cnt;
60              }
61          };
62      }
63  
64      @Override
65      protected <T> Collector<T, ArrayList<T>, IntMultimap<T>> collector() {
66          final Collector<Tuple2<Integer, T>, ArrayList<Tuple2<Integer, T>>, ? extends Multimap<Integer, T>> mapCollector = mapCollector();
67          return new Collector<T, ArrayList<T>, IntMultimap<T>>() {
68              @Override
69              public Supplier<ArrayList<T>> supplier() {
70                  return ArrayList::new;
71              }
72  
73              @Override
74              public BiConsumer<ArrayList<T>, T> accumulator() {
75                  return ArrayList::add;
76              }
77  
78              @Override
79              public BinaryOperator<ArrayList<T>> combiner() {
80                  return (left, right) -> fromTuples(mapCollector.combiner().apply(toTuples(left), toTuples(right)));
81              }
82  
83              @Override
84              public Function<ArrayList<T>, IntMultimap<T>> finisher() {
85                  return AbstractMultimapTest.this::ofAll;
86              }
87  
88              @Override
89              public java.util.Set<Characteristics> characteristics() {
90                  return mapCollector.characteristics();
91              }
92  
93              private ArrayList<Tuple2<Integer, T>> toTuples(java.util.List<T> list) {
94                  final ArrayList<Tuple2<Integer, T>> result = new ArrayList<>();
95                  Stream.ofAll(list)
96                          .zipWithIndex()
97                          .map(tu -> Tuple.of(tu._2, tu._1))
98                          .forEach(result::add);
99                  return result;
100             }
101 
102             private ArrayList<T> fromTuples(java.util.List<Tuple2<Integer, T>> list) {
103                 final ArrayList<T> result = new ArrayList<>();
104                 Stream.ofAll(list)
105                         .map(tu -> tu._2)
106                         .forEach(result::add);
107                 return result;
108             }
109         };
110     }
111 
112     @Override
113     protected <T> IntMultimap<T> empty() {
114         return IntMultimap.of(emptyMap());
115     }
116 
117     @Override
118     protected boolean emptyShouldBeSingleton() {
119         return false;
120     }
121 
122     private <T> Multimap<Integer, T> emptyInt() {
123         return emptyMap();
124     }
125 
126     protected Multimap<Integer, Integer> emptyIntInt() {
127         return emptyMap();
128     }
129 
130     private Multimap<Integer, String> emptyIntString() {
131         return emptyMap();
132     }
133 
134     abstract protected String className();
135 
136     abstract <T1, T2> java.util.Map<T1, T2> javaEmptyMap();
137 
138     protected String containerName() {
139         switch (containerType) {
140             case SEQ:
141                 return "List";
142             case SET:
143                 return "HashSet";
144             case SORTED_SET:
145                 return "TreeSet";
146             default:
147                 throw new RuntimeException();
148         }
149     }
150 
151     protected <T1 extends Comparable<T1>, T2> Multimap<T1, T2> emptyMap() {
152         return emptyMap(Comparators.naturalComparator());
153     }
154 
155     abstract protected <T1 extends Comparable<T1>, T2> Multimap<T1, T2> emptyMap(Comparator<? super T2> comparator);
156 
157     protected boolean emptyMapShouldBeSingleton() {
158         return true;
159     }
160 
161     abstract protected <T> Collector<Tuple2<Integer, T>, ArrayList<Tuple2<Integer, T>>, ? extends Multimap<Integer, T>> mapCollector();
162 
163     @SuppressWarnings("unchecked")
164     abstract protected <K extends Comparable<K>, V> Multimap<K, V> mapOfTuples(Tuple2<? extends K, ? extends V>... entries);
165 
166     @SuppressWarnings("unchecked")
167     abstract protected <K extends Comparable<K>, V> Multimap<K, V> mapOfEntries(java.util.Map.Entry<? extends K, ? extends V>... entries);
168 
169     abstract protected <K extends Comparable<K>, V> Multimap<K, V> mapOfPairs(K k1, V v1, K k, V v2, K k3, V v3);
170 
171     abstract protected <K extends Comparable<K>, V> Multimap<K, V> mapOf(K key, V value);
172 
173     abstract protected <K extends Comparable<? super K>, V> Multimap<K, V> mapOf(java.util.Map<? extends K, ? extends V> map);
174 
175     abstract protected <T, K extends Comparable<? super K>, V> Multimap<K, V> mapOf(java.util.stream.Stream<? extends T> stream,
176                                                                                     Function<? super T, ? extends K> keyMapper,
177                                                                                     Function<? super T, ? extends V> valueMapper);
178 
179     abstract protected <T, K extends Comparable<? super K>, V> Multimap<K, V> mapOf(java.util.stream.Stream<? extends T> stream,
180                                                                                     Function<? super T, Tuple2<? extends K, ? extends V>> f);
181 
182     abstract protected <K extends Comparable<K>, V> Multimap<K, V> mapTabulate(int n, Function<? super Integer, ? extends Tuple2<? extends K, ? extends V>> f);
183 
184     abstract protected <K extends Comparable<K>, V> Multimap<K, V> mapFill(int n, Supplier<? extends Tuple2<? extends K, ? extends V>> s);
185 
186     @Override
187     protected boolean useIsEqualToInsteadOfIsSameAs() {
188         return true;
189     }
190 
191     @Override
192     protected int getPeekNonNilPerformingAnAction() {
193         return 1;
194     }
195 
196     @Override
197     protected <T> IntMultimap<T> of(T element) {
198         Multimap<Integer, T> map = emptyMap();
199         map = map.put(0, element);
200         return IntMultimap.of(map);
201     }
202 
203     @SuppressWarnings("unchecked")
204     @Override
205     protected <T> IntMultimap<T> of(T... elements) {
206         Multimap<Integer, T> map = emptyMap();
207         for (T element : elements) {
208             map = map.put(map.size(), element);
209         }
210         return IntMultimap.of(map);
211     }
212 
213     @Override
214     protected <T> IntMultimap<T> ofAll(Iterable<? extends T> elements) {
215         Multimap<Integer, T> map = emptyMap();
216         int i = 0;
217         for (T element : elements) {
218             map = map.put(i++, element);
219         }
220         return IntMultimap.of(map);
221     }
222 
223     @Override
224     protected <T extends Comparable<? super T>> IntMultimap<T> ofJavaStream(java.util.stream.Stream<? extends T> javaStream) {
225         return ofAll(io.vavr.collection.Iterator.ofAll(javaStream.iterator()));
226     }
227 
228     @Override
229     protected IntMultimap<Boolean> ofAll(boolean... elements) {
230         return ofAll(io.vavr.collection.Iterator.ofAll(elements));
231     }
232 
233     @Override
234     protected IntMultimap<Byte> ofAll(byte... elements) {
235         return ofAll(io.vavr.collection.Iterator.ofAll(elements));
236     }
237 
238     @Override
239     protected IntMultimap<Character> ofAll(char... elements) {
240         return ofAll(io.vavr.collection.Iterator.ofAll(elements));
241     }
242 
243     @Override
244     protected IntMultimap<Double> ofAll(double... elements) {
245         return ofAll(io.vavr.collection.Iterator.ofAll(elements));
246     }
247 
248     @Override
249     protected IntMultimap<Float> ofAll(float... elements) {
250         return ofAll(io.vavr.collection.Iterator.ofAll(elements));
251     }
252 
253     @Override
254     protected IntMultimap<Integer> ofAll(int... elements) {
255         return ofAll(io.vavr.collection.Iterator.ofAll(elements));
256     }
257 
258     @Override
259     protected IntMultimap<Long> ofAll(long... elements) {
260         return ofAll(io.vavr.collection.Iterator.ofAll(elements));
261     }
262 
263     @Override
264     protected IntMultimap<Short> ofAll(short... elements) {
265         return ofAll(io.vavr.collection.Iterator.ofAll(elements));
266     }
267 
268     @Override
269     protected <T> IntMultimap<T> tabulate(int n, Function<? super Integer, ? extends T> f) {
270         Multimap<Integer, T> map = emptyMap();
271         for (int i = 0; i < n; i++) {
272             map = map.put(map.size(), f.apply(i));
273         }
274         return IntMultimap.of(map);
275     }
276 
277     @Override
278     protected <T> IntMultimap<T> fill(int n, Supplier<? extends T> s) {
279         return tabulate(n, anything -> s.get());
280     }
281 
282     // -- construction
283 
284     @Test
285     public void shouldBeTheSame() {
286         assertThat(mapOf(1, 2)).isEqualTo(emptyIntInt().put(1, 2));
287     }
288 
289     private static java.util.Map.Entry<String, Integer> entry(String key, Integer value) {
290         return new java.util.AbstractMap.SimpleEntry<>(key, value);
291     }
292 
293     @SuppressWarnings("unchecked")
294     @Test
295     public void shouldConstructFromEntries() {
296         final Multimap<String, Integer> map = mapOfEntries(entry("1", 1), entry("2", 2), entry("3", 3));
297         assertThat(map).isEqualTo(this.<String, Integer> emptyMap().put("1", 1).put("2", 2).put("3", 3));
298     }
299 
300     @Test
301     public void shouldConstructFromPairs() {
302         final Multimap<String, Integer> map = mapOfPairs("1", 1, "2", 2, "3", 3);
303         assertThat(map).isEqualTo(this.<String, Integer> emptyMap().put("1", 1).put("2", 2).put("3", 3));
304     }
305 
306     @Test
307     public void shouldConstructFromJavaStream() {
308         final java.util.stream.Stream<Integer> javaStream = java.util.stream.Stream.of(1, 2, 3);
309         final Multimap<String, Integer> map = mapOf(javaStream, String::valueOf, Function.identity());
310         assertThat(map).isEqualTo(this.<String, Integer> emptyMap().put("1", 1).put("2", 2).put("3", 3));
311     }
312 
313     @Test
314     public void shouldConstructFromJavaStreamEntries() {
315         final java.util.stream.Stream<Integer> javaStream = java.util.stream.Stream.of(1, 2, 3);
316         final Multimap<String, Integer> map = mapOf(javaStream, i -> Tuple.of(String.valueOf(i), i));
317         assertThat(map).isEqualTo(this.<String, Integer> emptyMap().put("1", 1).put("2", 2).put("3", 3));
318     }
319 
320     @Test
321     public void shouldConstructFromJavaMap() {
322         final java.util.Map<String, Integer> source = new java.util.HashMap<>();
323         source.put("1", 2);
324         source.put("3", 4);
325         final Multimap<String, Integer> map = mapOf(source);
326         assertThat(map).isEqualTo(this.<String, Integer> emptyMap().put("1", 2).put("3", 4));
327     }
328 
329     // -- toString
330 
331     @Test
332     public void shouldMakeString() {
333         assertThat(emptyMap().toString()).isEqualTo(className() + "()");
334         assertThat(emptyIntInt().put(1, 2).toString()).isEqualTo(className() + "(" + Tuple.of(1, 2) + ")");
335     }
336 
337     // -- toJavaMap
338 
339     @Test
340     public void shouldConvertToJavaMap() {
341         final Multimap<String, Integer> vavr = mapOfPairs("1", 1, "2", 2, "3", 3);
342         final java.util.Map<String, java.util.Collection<Integer>> java = javaEmptyMap();
343         java.put("1", javaListOf(1));
344         java.put("2", javaListOf(2));
345         java.put("3", javaListOf(3));
346         assertThat(vavr.toJavaMap()).isEqualTo(java);
347     }
348 
349     private java.util.Collection<Integer> javaListOf(Integer i) {
350         final java.util.Collection<Integer> list;
351         switch (containerType) {
352             case SEQ:
353                 list = new ArrayList<>();
354                 break;
355             case SET:
356                 list = new java.util.HashSet<>();
357                 break;
358             case SORTED_SET:
359                 list = new java.util.TreeSet<>();
360                 break;
361             default:
362                 throw new RuntimeException();
363         }
364         list.add(i);
365         return list;
366     }
367 
368     // -- apply
369 
370     @Test
371     public void shouldApplyExistingKey() {
372         assertThat(emptyIntInt().put(1, 2).apply(1)).isEqualTo(io.vavr.collection.HashSet.of(2));
373     }
374 
375     @Test(expected = NoSuchElementException.class)
376     public void shouldApplyNonExistingKey() {
377         emptyIntInt().put(1, 2).apply(3);
378     }
379 
380     // -- contains
381 
382     @Test
383     public void shouldFindKey() {
384         assertThat(emptyIntInt().put(1, 2).containsKey(1)).isTrue();
385         assertThat(emptyIntInt().put(1, 2).containsKey(2)).isFalse();
386     }
387 
388     @Test
389     public void shouldFindValue() {
390         assertThat(emptyIntInt().put(1, 2).containsValue(2)).isTrue();
391         assertThat(emptyIntInt().put(1, 2).containsValue(1)).isFalse();
392     }
393 
394     @Test
395     public void shouldRecognizeNotContainedKeyValuePair() {
396         final Multimap<String, Integer> testee = mapOf("one", 1);
397         assertThat(testee.contains(Tuple.of("one", 0))).isFalse();
398     }
399 
400     @Test
401     public void shouldRecognizeContainedKeyValuePair() {
402         final Multimap<String, Integer> testee = mapOf("one", 1);
403         assertThat(testee.contains(Tuple.of("one", 1))).isTrue();
404     }
405 
406     // -- equality
407 
408     @Test
409     public void shouldObeyEqualityConstraints() {
410 
411         // sequential collections
412         assertThat(emptyMap().equals(HashMultimap.withSeq().empty())).isTrue();
413         assertThat(mapOf(1, "a").equals(HashMultimap.withSeq().of(1, "a"))).isTrue();
414         assertThat(mapOfPairs(1, "a", 2, "b", 3, "c").equals(HashMultimap.withSeq().of(1, "a", 2, "b",3, "c"))).isTrue();
415         assertThat(mapOfPairs(1, "a", 2, "b", 3, "c").equals(HashMultimap.withSeq().of(3, "c", 2, "b",1, "a"))).isTrue();
416 
417         // other classes
418         assertThat(empty().equals(io.vavr.collection.List.empty())).isFalse();
419         assertThat(empty().equals(HashMap.empty())).isFalse();
420         assertThat(empty().equals(io.vavr.collection.HashSet.empty())).isFalse();
421 
422         assertThat(empty().equals(LinkedHashMap.empty())).isFalse();
423         assertThat(empty().equals(io.vavr.collection.LinkedHashSet.empty())).isFalse();
424 
425         assertThat(empty().equals(TreeMap.empty())).isFalse();
426         assertThat(empty().equals(TreeSet.empty())).isFalse();
427     }
428 
429     // -- flatMap
430 
431     @SuppressWarnings("unchecked")
432     @Test
433     public void shouldFlatMapUsingBiFunction() {
434         final Multimap<Integer, Integer> testee = mapOfTuples(Tuple.of(1, 11), Tuple.of(2, 22), Tuple.of(3, 33));
435         final Multimap<String, String> actual = testee
436                 .flatMap((k, v) -> io.vavr.collection.List.of(Tuple.of(String.valueOf(k), String.valueOf(v)),
437                         Tuple.of(String.valueOf(k * 10), String.valueOf(v * 10))));
438         final Multimap<String, String> expected = mapOfTuples(Tuple.of("1", "11"), Tuple.of("10", "110"), Tuple.of("2", "22"),
439                 Tuple.of("20", "220"), Tuple.of("3", "33"), Tuple.of("30", "330"));
440         assertThat(actual).isEqualTo(expected);
441     }
442 
443     // -- keySet
444 
445     @SuppressWarnings("unchecked")
446     @Test
447     public void shouldReturnsKeySet() {
448         final Set<Integer> actual = mapOfTuples(Tuple.of(1, 11), Tuple.of(2, 22), Tuple.of(3, 33)).keySet();
449         assertThat(actual).isEqualTo(io.vavr.collection.HashSet.of(1, 2, 3));
450     }
451 
452     // -- biMap
453 
454     @Test
455     public void shouldBiMapEmpty() {
456         assertThat(emptyInt().bimap(i -> i + 1, o -> o)).isEqualTo(io.vavr.collection.Vector.empty());
457     }
458 
459     @Test
460     public void shouldBiMapNonEmpty() {
461         final Seq<Tuple2<Integer, String>> expected = Stream.of(Tuple.of(2, "1!"), Tuple.of(3, "2!"));
462         final Seq<Tuple2<Integer, String>> actual = emptyInt().put(1, "1").put(2, "2").bimap(i -> i + 1, s -> s + "!").toStream();
463         assertThat(actual).isEqualTo(expected);
464     }
465 
466     // -- map
467 
468     @Test
469     public void shouldMapEmpty() {
470         assertThat(emptyInt().map(Tuple2::_1)).isEqualTo(io.vavr.collection.Vector.empty());
471     }
472 
473     @Test
474     public void shouldMapNonEmpty() {
475         final Seq<Integer> expected = io.vavr.collection.Vector.of(1, 2);
476         final Seq<Integer> actual = emptyInt().put(1, "1").put(2, "2").map(Tuple2::_1);
477         assertThat(actual).isEqualTo(expected);
478     }
479 
480     @Test
481     public void shouldMapComparableValues() {
482         final Multimap<Integer, String> map = this.<Integer, String>emptyMap()
483                 .put(1, "1")
484                 .put(1, "2")
485                 .put(2, "3");
486         assertThat(map.map(v -> v)).isEqualTo(io.vavr.collection.List.of(
487                 Tuple.of(1, "1"),
488                 Tuple.of(1, "2"),
489                 Tuple.of(2, "3")));
490     }
491 
492     @Test
493     public void shouldMapIncomparableValues() {
494         final Multimap<Integer, Incomparable> map = this.<Integer, Incomparable>emptyMap(Comparator.comparing(Incomparable::getS))
495                 .put(1, new Incomparable("1"))
496                 .put(1, new Incomparable("2"))
497                 .put(2, new Incomparable("3"));
498         assertThat(map.map(v -> v)).isEqualTo(io.vavr.collection.List.of(
499                 Tuple.of(1, new Incomparable("1")),
500                 Tuple.of(1, new Incomparable("2")),
501                 Tuple.of(2, new Incomparable("3"))));
502     }
503 
504     private final static class Incomparable {
505         private String s;
506 
507         Incomparable(String s) {
508             this.s = s;
509         }
510 
511         public String getS() {
512             return s;
513         }
514 
515         @Override
516         public boolean equals(Object o) {
517             return o == this || (o instanceof Incomparable && Objects.equals(s, ((Incomparable) o).s));
518         }
519 
520         @Override
521         public int hashCode() {
522             return Objects.hashCode(s);
523         }
524     }
525 
526     @Test
527     public void shouldReturnEmptySetWhenAskedForTuple2SetOfAnEmptyMap() {
528         assertThat(emptyMap().toSet()).isEqualTo(io.vavr.collection.HashSet.empty());
529     }
530 
531     @Test
532     public void shouldReturnTuple2SetOfANonEmptyMap() {
533         assertThat(emptyInt().put(1, "1").put(2, "2").toSet()).isEqualTo(io.vavr.collection.HashSet.of(Tuple.of(1, "1"), Tuple.of(2, "2")));
534     }
535 
536     @Test
537     public void shouldReturnModifiedValuesMap() {
538         assertThat(emptyIntString().put(1, "1").put(2, "2").mapValues(Integer::parseInt)).isEqualTo(emptyIntInt().put(1, 1).put(2, 2));
539     }
540 
541     @Test
542     public void shouldReturnListWithMappedValues() {
543         assertThat(emptyIntInt().put(1, 1).put(2, 2).iterator((a, b) -> a + b)).isEqualTo(io.vavr.collection.List.of(2, 4));
544     }
545 
546     // -- merge
547 
548     @Test
549     public void shouldMerge() {
550         final Multimap<Integer, Integer> m1 = emptyIntInt().put(1, 1).put(2, 2);
551         final Multimap<Integer, Integer> m2 = emptyIntInt().put(1, 1).put(4, 4);
552         final Multimap<Integer, Integer> m3 = emptyIntInt().put(3, 3).put(4, 4);
553         assertThat(emptyIntInt().merge(m2)).isEqualTo(m2);
554         assertThat(m2.merge(emptyIntInt())).isEqualTo(m2);
555         if (containerType == Multimap.ContainerType.SEQ) {
556             assertThat(m1.merge(m2)).isEqualTo(emptyIntInt().put(1, 1).put(1, 1).put(2, 2).put(4, 4));
557             assertThat(m1.merge(m3)).isEqualTo(emptyIntInt().put(1, 1).put(2, 2).put(3, 3).put(4, 4));
558         } else {
559             assertThat(m1.merge(m2)).isEqualTo(emptyIntInt().put(1, 1).put(2, 2).put(4, 4));
560             assertThat(m1.merge(m3)).isEqualTo(emptyIntInt().put(1, 1).put(2, 2).put(3, 3).put(4, 4));
561         }
562     }
563 
564     @SuppressWarnings("unchecked")
565     @Test
566     public void shouldMergeCollisions() {
567         final Multimap<Integer, Integer> m1 = emptyIntInt().put(1, 1).put(2, 2);
568         final Multimap<Integer, Integer> m2 = emptyIntInt().put(1, 2).put(4, 4);
569         final Multimap<Integer, Integer> m3 = emptyIntInt().put(3, 3).put(4, 4);
570         assertThat(emptyIntInt().merge(m2, (s1, s2) -> io.vavr.collection.Iterator.concat(s1, s2))).isEqualTo(m2);
571         assertThat(m2.merge(emptyIntInt(), (s1, s2) -> io.vavr.collection.Iterator.concat(s1, s2))).isEqualTo(m2);
572         assertThat(m1.merge(m2, (s1, s2) -> io.vavr.collection.Iterator.concat(s1, s2))).isEqualTo(emptyIntInt().put(1, 1).put(1, 2).put(2, 2).put(4, 4));
573         assertThat(m1.merge(m3, (s1, s2) -> io.vavr.collection.Iterator.concat(s1, s2))).isEqualTo(emptyIntInt().put(1, 1).put(2, 2).put(3, 3).put(4, 4));
574     }
575 
576     // -- orElse
577     // DEV-Note: IntMultimap converts `other` to multimap
578 
579     @Override
580     @Test
581     public void shouldCaclEmptyOrElseSameOther() {
582         Iterable<Integer> other = of(42);
583         assertThat(empty().orElse(other)).isEqualTo(other);
584     }
585 
586     @Test
587     public void shouldCaclEmptyOrElseSameSupplier() {
588         Iterable<Integer> other = of(42);
589         Supplier<Iterable<Integer>> supplier = () -> other;
590         assertThat(empty().orElse(supplier)).isEqualTo(other);
591     }
592 
593     // -- equality
594 
595     @Test
596     public void shouldIgnoreOrderOfEntriesWhenComparingForEquality() {
597         final Multimap<?, ?> map1 = emptyInt().put(1, 'a').put(2, 'b').put(3, 'c');
598         final Multimap<?, ?> map2 = emptyInt().put(3, 'c').put(2, 'b').put(1, 'a').remove(2).put(2, 'b');
599         assertThat(map1).isEqualTo(map2);
600     }
601 
602     // -- put
603 
604     @Test
605     public void shouldPutTuple() {
606         assertThat(emptyIntInt().put(Tuple.of(1, 2))).isEqualTo(emptyIntInt().put(1, 2));
607     }
608 
609     // -- remove
610 
611     @Test
612     public void shouldRemoveKey() {
613         final Multimap<Integer, Object> src = emptyInt().put(1, 'a').put(2, 'b').put(3, 'c');
614         assertThat(src.remove(2)).isEqualTo(emptyInt().put(1, 'a').put(3, 'c'));
615         assertThat(src.remove(33)).isSameAs(src);
616     }
617 
618     // -- replace
619 
620     @Test
621     public void shouldReplaceEntity() {
622         final Multimap<Integer, Object> actual = emptyInt().put(1, "a").put(1, "b").replace(Tuple.of(1, "a"), Tuple.of(1, "c"));
623         final Multimap<Integer, Object> expected = emptyInt().put(1, "c").put(1, "b");
624         assertThat(actual).isEqualTo(expected);
625     }
626 
627     // -- removeAll
628 
629     @Test
630     public void shouldRemoveAllKeys() {
631         final Multimap<Integer, Object> src = emptyInt().put(1, 'a').put(2, 'b').put(3, 'c');
632         assertThat(src.removeAll(io.vavr.collection.List.of(1, 3))).isEqualTo(emptyInt().put(2, 'b'));
633         assertThat(src.removeAll(io.vavr.collection.List.of(33))).isSameAs(src);
634         assertThat(src.removeAll(io.vavr.collection.List.empty())).isSameAs(src);
635     }
636 
637     // -- replaceValue
638 
639     @Test
640     public void shouldReturnSameInstanceIfReplacingCurrentValueWithNonExistingKey() {
641         final Multimap<Integer, String> map = mapOf(1, "a").put(2, "b");
642         final Multimap<Integer, String> actual = map.replaceValue(3, "?");
643         assertThat(actual).isSameAs(map);
644     }
645 
646     @Test
647     public void shouldReplaceCurrentValueForExistingKey() {
648         final Multimap<Integer, String> map = mapOf(1, "a").put(2, "b");
649         final Multimap<Integer, String> actual = map.replaceValue(2, "c");
650         final Multimap<Integer, String> expected = mapOf(1, "a").put(2, "c");
651         assertThat(actual).isEqualTo(expected);
652     }
653 
654     @Test
655     public void shouldReplaceValuesWithNewValueForExistingKey() {
656         final Multimap<Integer, String> map = mapOf(1, "a").put(2, "b").put(2, "c");
657         final Multimap<Integer, String> actual = map.replaceValue(2, "c");
658         final Multimap<Integer, String> expected = mapOf(1, "a").put(2, "c");
659         assertThat(actual).isEqualTo(expected);
660     }
661 
662     // - replace
663 
664     @Test
665     public void shouldReplaceCurrentValueForExistingKeyAndEqualOldValue() {
666         final Multimap<Integer, String> map = mapOf(1, "a").put(2, "b");
667         final Multimap<Integer, String> actual = map.replace(2, "b", "c");
668         final Multimap<Integer, String> expected = mapOf(1, "a").put(2, "c");
669         assertThat(actual).isEqualTo(expected);
670     }
671 
672     @Test
673     public void shouldReplaceCurrentValueForKeyWithMultipleValuesAndEqualOldValue() {
674         final Multimap<Integer, String> map = mapOf(1, "a").put(2, "b").put(2, "d");
675         final Multimap<Integer, String> actual = map.replace(2, "b", "c");
676         final Multimap<Integer, String> expected = mapOf(1, "a").put(2, "c").put(2, "d");
677         assertThat(actual).isEqualTo(expected);
678     }
679 
680     @Test
681     public void shouldReturnSameInstanceForExistingKeyAndNonEqualOldValue() {
682         final Multimap<Integer, String> map = mapOf(1, "a").put(2, "b");
683         final Multimap<Integer, String> actual = map.replace(2, "d", "c");
684         assertThat(actual).isSameAs(map);
685     }
686 
687     @Test
688     public void shouldReturnSameInstanceIfReplacingCurrentValueWithOldValueWithNonExistingKey() {
689         final Multimap<Integer, String> map = mapOf(1, "a").put(2, "b");
690         final Multimap<Integer, String> actual = map.replace(3, "?", "!");
691         assertThat(actual).isSameAs(map);
692     }
693 
694     // - replaceAll
695 
696     @Test
697     public void shouldReplaceAllValuesWithFunctionResult() {
698         final Multimap<Integer, String> map = mapOf(1, "a").put(2, "b").put(2, "c");
699         final Multimap<Integer, String> actual = map.replaceAll((integer, s) -> s + integer);
700         final Multimap<Integer, String> expected = mapOf(1, "a1").put(2, "b2").put(2, "c2");
701         assertThat(actual).isEqualTo(expected);
702     }
703 
704     // -- transform
705 
706     @Test
707     public void shouldTransform() {
708         final Multimap<?, ?> actual = emptyIntInt().put(1, 11).transform(map -> map.put(2, 22));
709         assertThat(actual).isEqualTo(emptyIntInt().put(1, 11).put(2, 22));
710     }
711 
712     // -- unzip
713 
714     @Test
715     public void shouldUnzipNil() {
716         assertThat(emptyMap().unzip(x -> Tuple.of(x, x))).isEqualTo(Tuple.of(Stream.empty(), Stream.empty()));
717         assertThat(emptyMap().unzip((k, v) -> Tuple.of(Tuple.of(k, v), Tuple.of(k, v))))
718                 .isEqualTo(Tuple.of(Stream.empty(), Stream.empty()));
719     }
720 
721     @Test
722     public void shouldUnzipNonNil() {
723         final Multimap<Integer, Integer> map = emptyIntInt().put(0, 0).put(1, 1);
724         final Tuple actual = map.unzip(entry -> Tuple.of(entry._1, entry._2 + 1));
725         final Tuple expected = Tuple.of(Stream.of(0, 1), Stream.of(1, 2));
726         assertThat(actual).isEqualTo(expected);
727     }
728 
729     @Test
730     public void shouldUnzip3Nil() {
731         assertThat(emptyMap().unzip3(x -> Tuple.of(x, x, x))).isEqualTo(Tuple.of(Stream.empty(), Stream.empty(), Stream.empty()));
732         assertThat(emptyMap().unzip3((k, v) -> Tuple.of(Tuple.of(k, v), Tuple.of(k, v), Tuple.of(k, v))))
733                 .isEqualTo(Tuple.of(Stream.empty(), Stream.empty(), Stream.empty()));
734     }
735 
736     @Test
737     public void shouldUnzip3NonNil() {
738         final Multimap<Integer, Integer> map = emptyIntInt().put(0, 0).put(1, 1);
739         final Tuple actual = map.unzip3(entry -> Tuple.of(entry._1, entry._2 + 1, entry._2 + 5));
740         final Tuple expected = Tuple.of(Stream.of(0, 1), Stream.of(1, 2), Stream.of(5, 6));
741         assertThat(actual).isEqualTo(expected);
742     }
743 
744     // -- zip
745 
746     @Test
747     public void shouldZipNils() {
748         final Seq<Tuple2<Tuple2<Integer, Integer>, Object>> actual = emptyIntInt().zip(io.vavr.collection.List.empty());
749         assertThat(actual).isEqualTo(Stream.empty());
750     }
751 
752     @Test
753     public void shouldZipEmptyAndNonNil() {
754         final Seq<Tuple2<Tuple2<Integer, Integer>, Integer>> actual = emptyIntInt().zip(io.vavr.collection.List.of(1));
755         assertThat(actual).isEqualTo(Stream.empty());
756     }
757 
758     @Test
759     public void shouldZipNonEmptyAndNil() {
760         final Seq<Tuple2<Tuple2<Integer, Integer>, Object>> actual = emptyIntInt().put(0, 1).zip(io.vavr.collection.List.empty());
761         assertThat(actual).isEqualTo(Stream.empty());
762     }
763 
764     @Test
765     public void shouldZipNonNilsIfThisIsSmaller() {
766         final Seq<Tuple2<Tuple2<Integer, Integer>, Integer>> actual = emptyIntInt()
767                 .put(0, 0)
768                 .put(1, 1)
769                 .zip(io.vavr.collection.List.of(5, 6, 7));
770         assertThat(actual).isEqualTo(Stream.of(Tuple.of(Tuple.of(0, 0), 5), Tuple.of(Tuple.of(1, 1), 6)));
771     }
772 
773     @Test
774     public void shouldZipNonNilsIfThatIsSmaller() {
775         final Seq<Tuple2<Tuple2<Integer, Integer>, Integer>> actual = emptyIntInt()
776                 .put(0, 0)
777                 .put(1, 1)
778                 .put(2, 2)
779                 .zip(io.vavr.collection.List.of(5, 6));
780         assertThat(actual).isEqualTo(Stream.of(Tuple.of(Tuple.of(0, 0), 5), Tuple.of(Tuple.of(1, 1), 6)));
781     }
782 
783     @Test
784     public void shouldZipNonNilsOfSameSize() {
785         final Seq<Tuple2<Tuple2<Integer, Integer>, Integer>> actual = emptyIntInt()
786                 .put(0, 0)
787                 .put(1, 1)
788                 .put(2, 2)
789                 .zip(io.vavr.collection.List.of(5, 6, 7));
790         assertThat(actual).isEqualTo(
791                 Stream.of(Tuple.of(Tuple.of(0, 0), 5), Tuple.of(Tuple.of(1, 1), 6), Tuple.of(Tuple.of(2, 2), 7)));
792     }
793 
794     @Test(expected = NullPointerException.class)
795     public void shouldThrowIfZipWithThatIsNull() {
796         emptyMap().zip(null);
797     }
798 
799     // -- zipWithIndex
800 
801     @Test
802     public void shouldZipNilWithIndex() {
803         assertThat(emptyMap().zipWithIndex()).isEqualTo(Stream.empty());
804     }
805 
806     @Test
807     public void shouldZipNonNilWithIndex() {
808         final Seq<Tuple2<Tuple2<Integer, Integer>, Integer>> actual = emptyIntInt()
809                 .put(0, 0)
810                 .put(1, 1)
811                 .put(2, 2)
812                 .zipWithIndex();
813         assertThat(actual).isEqualTo(
814                 Stream.of(Tuple.of(Tuple.of(0, 0), 0), Tuple.of(Tuple.of(1, 1), 1), Tuple.of(Tuple.of(2, 2), 2)));
815     }
816 
817     // -- zipAll
818 
819     @Test
820     public void shouldZipAllNils() {
821         final Seq<Tuple2<Tuple2<Integer, Integer>, Object>> actual = emptyIntInt().zipAll(empty(), null, null);
822         assertThat(actual).isEqualTo(Stream.empty());
823     }
824 
825     @Test
826     public void shouldZipAllEmptyAndNonNil() {
827         final Seq<Tuple2<Tuple2<Integer, Integer>, Object>> actual = emptyIntInt().zipAll(io.vavr.collection.List.of(1), null, null);
828         assertThat(actual).isEqualTo(Stream.of(Tuple.of(null, 1)));
829     }
830 
831     @Test
832     public void shouldZipAllNonEmptyAndNil() {
833         final Seq<Tuple2<Tuple2<Integer, Integer>, Object>> actual = emptyIntInt().put(0, 1).zipAll(empty(), null, null);
834         assertThat(actual).isEqualTo(Stream.of(Tuple.of(Tuple.of(0, 1), null)));
835     }
836 
837     @Test
838     public void shouldZipAllNonNilsIfThisIsSmaller() {
839         final Seq<Tuple2<Tuple2<Integer, Object>, String>> actual = this.<Integer, Object> emptyMap()
840                 .put(1, 1)
841                 .put(2, 2)
842                 .zipAll(of("a", "b", "c"), Tuple.of(9, 10), "z");
843         final Seq<Tuple2<Tuple2<Object, Object>, String>> expected = Stream.of(Tuple.of(Tuple.of(1, 1), "a"),
844                 Tuple.of(Tuple.of(2, 2), "b"), Tuple.of(Tuple.of(9, 10), "c"));
845         assertThat(actual).isEqualTo(expected);
846     }
847 
848     @Test
849     public void shouldZipAllNonNilsIfThisIsMoreSmaller() {
850         final Seq<Tuple2<Tuple2<Integer, Object>, String>> actual = this.<Integer, Object> emptyMap()
851                 .put(1, 1)
852                 .put(2, 2)
853                 .zipAll(of("a", "b", "c", "d"), Tuple.of(9, 10), "z");
854         final Seq<Tuple2<Tuple2<Object, Object>, String>> expected = Stream.of(Tuple.of(Tuple.of(1, 1), "a"),
855                 Tuple.of(Tuple.of(2, 2), "b"), Tuple.of(Tuple.of(9, 10), "c"), Tuple.of(Tuple.of(9, 10), "d"));
856         assertThat(actual).isEqualTo(expected);
857     }
858 
859     @Test
860     public void shouldZipAllNonNilsIfThatIsSmaller() {
861         final Seq<Tuple2<Tuple2<Integer, Object>, String>> actual = this.<Integer, Object> emptyMap()
862                 .put(1, 1)
863                 .put(2, 2)
864                 .put(3, 3)
865                 .zipAll(this.of("a", "b"), Tuple.of(9, 10), "z");
866         final Seq<Tuple2<Tuple2<Object, Object>, String>> expected = Stream.of(Tuple.of(Tuple.of(1, 1), "a"),
867                 Tuple.of(Tuple.of(2, 2), "b"), Tuple.of(Tuple.of(3, 3), "z"));
868         assertThat(actual).isEqualTo(expected);
869     }
870 
871     @Test
872     public void shouldZipAllNonNilsIfThatIsMoreSmaller() {
873         final Seq<Tuple2<Tuple2<Integer, Object>, String>> actual = this.<Integer, Object> emptyMap()
874                 .put(1, 1)
875                 .put(2, 2)
876                 .put(3, 3)
877                 .put(4, 4)
878                 .zipAll(of("a", "b"), Tuple.of(9, 10), "z");
879         final Seq<Tuple2<Tuple2<Object, Object>, String>> expected = Stream.of(Tuple.of(Tuple.of(1, 1), "a"),
880                 Tuple.of(Tuple.of(2, 2), "b"), Tuple.of(Tuple.of(3, 3), "z"), Tuple.of(Tuple.of(4, 4), "z"));
881         assertThat(actual).isEqualTo(expected);
882     }
883 
884     @Test
885     public void shouldZipAllNonNilsOfSameSize() {
886         final Seq<Tuple2<Tuple2<Integer, Object>, String>> actual = this.<Integer, Object> emptyMap()
887                 .put(1, 1)
888                 .put(2, 2)
889                 .put(3, 3)
890                 .zipAll(of("a", "b", "c"), Tuple.of(9, 10), "z");
891         final Seq<Tuple2<Tuple2<Object, Object>, String>> expected = Stream.of(Tuple.of(Tuple.of(1, 1), "a"),
892                 Tuple.of(Tuple.of(2, 2), "b"), Tuple.of(Tuple.of(3, 3), "c"));
893         assertThat(actual).isEqualTo(expected);
894     }
895 
896     @Test(expected = NullPointerException.class)
897     public void shouldThrowIfZipAllWithThatIsNull() {
898         emptyMap().zipAll(null, null, null);
899     }
900 
901     // -- special cases
902 
903     @Override
904     public void shouldComputeDistinctOfNonEmptyTraversable() {
905         final Multimap<Integer, Object> testee = this.<Integer, Object> emptyMap().put(1, 1).put(2, 2).put(3, 3);
906         assertThat(testee.distinct()).isEqualTo(testee);
907     }
908 
909     @Override
910     public void shouldReturnSomeTailWhenCallingTailOptionOnNonNil() {
911         assertThat(of(1, 2, 3).tailOption().get()).isEqualTo(Option.some(of(2, 3)).get());
912     }
913 
914     @Override
915     public void shouldPreserveSingletonInstanceOnDeserialization() {
916         // The empty Multimap encapsulates a container type and map type and therefore cannot be a singleton
917     }
918 
919     @Override
920     public void shouldFoldRightNonNil() {
921         final String actual = of('a', 'b', 'c').foldRight("", (x, xs) -> x + xs);
922         final io.vavr.collection.List<String> expected = io.vavr.collection.List.of('a', 'b', 'c').permutations().map(io.vavr.collection.List::mkString);
923         assertThat(actual).isIn(expected);
924     }
925 
926     // -- forEach
927 
928     @Test
929     public void forEachByKeyValue() {
930         final Multimap<Integer, Integer> map = mapOf(1, 2).put(3, 4);
931         final int[] result = { 0 };
932         map.forEach((k, v) -> result[0] += k + v);
933         assertThat(result[0]).isEqualTo(10);
934     }
935 
936     @Test
937     public void forEachByTuple() {
938         final Multimap<Integer, Integer> map = mapOf(1, 2).put(3, 4);
939         final int[] result = { 0 };
940         map.forEach(t -> result[0] += t._1 + t._2);
941         assertThat(result[0]).isEqualTo(10);
942     }
943 
944     @SuppressWarnings("unchecked")
945     @Test
946     public void shouldTabulateTheSeq() {
947         final Function<Number, Tuple2<Long, Float>> f = i -> new Tuple2<>(i.longValue(), i.floatValue());
948         final Multimap<Long, Float> map = mapTabulate(3, f);
949         assertThat(map).isEqualTo(mapOfTuples(new Tuple2<>(0L, 0f), new Tuple2<>(1L, 1f), new Tuple2<>(2L, 2f)));
950     }
951 
952     @SuppressWarnings("unchecked")
953     @Test
954     public void shouldTabulateTheSeqCallingTheFunctionInTheRightOrder() {
955         final LinkedList<Integer> ints = new LinkedList<>(asList(0, 0, 1, 1, 2, 2));
956         final Function<Integer, Tuple2<Long, Float>> f = i -> new Tuple2<>(ints.remove().longValue(), ints.remove().floatValue());
957         final Multimap<Long, Float> map = mapTabulate(3, f);
958         assertThat(map).isEqualTo(mapOfTuples(new Tuple2<>(0L, 0f), new Tuple2<>(1L, 1f), new Tuple2<>(2L, 2f)));
959     }
960 
961     @Test
962     public void shouldTabulateTheSeqWith0Elements() {
963         assertThat(mapTabulate(0, i -> new Tuple2<>(i, i))).isEqualTo(empty());
964     }
965 
966     @Test
967     public void shouldTabulateTheSeqWith0ElementsWhenNIsNegative() {
968         assertThat(mapTabulate(-1, i -> new Tuple2<>(i, i))).isEqualTo(empty());
969     }
970 
971     @SuppressWarnings("unchecked")
972     @Test
973     public void shouldFillTheSeqCallingTheSupplierInTheRightOrder() {
974         final LinkedList<Integer> ints = new LinkedList<>(asList(0, 0, 1, 1, 2, 2));
975         final Supplier<Tuple2<Long, Float>> s = () -> new Tuple2<>(ints.remove().longValue(), ints.remove().floatValue());
976         final Multimap<Long, Float> actual = mapFill(3, s);
977         assertThat(actual).isEqualTo(mapOfTuples(new Tuple2<>(0L, 0f), new Tuple2<>(1L, 1f), new Tuple2<>(2L, 2f)));
978     }
979 
980     @Test
981     public void shouldFillTheSeqWith0Elements() {
982         assertThat(mapFill(0, () -> new Tuple2<>(1, 1))).isEqualTo(empty());
983     }
984 
985     @Test
986     public void shouldFillTheSeqWith0ElementsWhenNIsNegative() {
987         assertThat(mapFill(-1, () -> new Tuple2<>(1, 1))).isEqualTo(empty());
988     }
989 
990     /////////////////////////////////////////////////////////////////
991 
992     @Test
993     public void shouldHoldEqualsElements() {
994         Multimap<Integer, String> multimap = emptyMap();
995         multimap = multimap.put(1, "a").put(1, "b").put(1, "b");
996         if (containerType == Multimap.ContainerType.SEQ) {
997             assertThat(multimap.toString()).isEqualTo(className() + "((1, a), (1, b), (1, b))");
998         } else {
999             assertThat(multimap.toString()).isEqualTo(className() + "((1, a), (1, b))");
1000         }
1001     }
1002 
1003     // -- filter
1004 
1005     @Test
1006     public void shouldBiFilterWork() throws Exception {
1007         final Multimap<Integer, String> src = mapTabulate(20, n -> Tuple.of(n % 10, Integer.toHexString(n)));
1008         final Pattern isDigits = Pattern.compile("^\\d+$");
1009         final Multimap<Integer, String> dst = src.filter((k, v) -> k % 2 == 0 && isDigits.matcher(v).matches());
1010         assertThat(dst).isEqualTo(emptyIntString().put(0, "0").put(2, "2").put(4, "4").put(6, "6").put(6, "10").put(8, "8").put(8, "12"));
1011     }
1012 
1013     @Test
1014     public void shouldKeyFilterWork() throws Exception {
1015         final Multimap<Integer, String> src = mapTabulate(20, n -> Tuple.of(n % 10, Integer.toHexString(n)));
1016         final Multimap<Integer, String> dst = src.filterKeys(k -> k % 2 == 0);
1017         assertThat(dst).isEqualTo(emptyIntString().put(0, "0").put(0, "a").put(2, "2").put(2, "c").put(4, "4").put(4, "e").put(6, "6").put(6, "10").put(8, "8").put(8, "12"));
1018     }
1019 
1020     @Test
1021     public void shouldValueFilterWork() throws Exception {
1022         final Multimap<Integer, String> src = mapTabulate(10, n -> Tuple.of(n % 5, Integer.toHexString(n)));
1023         final Pattern isDigits = Pattern.compile("^\\d+$");
1024         final Multimap<Integer, String> dst = src.filterValues(v -> isDigits.matcher(v).matches());
1025         assertThat(dst).isEqualTo(emptyIntString().put(0, "0").put(0, "5").put(1, "1").put(1, "6").put(2, "2").put(2, "7").put(3, "3").put(3, "8").put(4, "4").put(4, "9"));
1026     }
1027 
1028     // -- remove by filter
1029 
1030     @Test
1031     public void shouldBiRemoveWork() throws Exception {
1032         final Multimap<Integer, String> src = mapTabulate(20, n -> Tuple.of(n % 10, Integer.toHexString(n)));
1033         final Pattern isDigits = Pattern.compile("^\\d+$");
1034         final Multimap<Integer, String> dst = src.removeAll((k, v) -> k % 2 == 0 && isDigits.matcher(v).matches());
1035         assertThat(dst).isEqualTo(emptyIntString().put(0, "a").put(1, "1").put(1, "b").put(2, "c").put(3, "3").put(3, "d").put(4, "e").put(5, "5").put(5, "f").put(7, "7").put(7, "11").put(9, "9").put(9, "13"));
1036     }
1037 
1038     @Test
1039     public void shouldKeyRemoveWork() throws Exception {
1040         final Multimap<Integer, String> src = mapTabulate(20, n -> Tuple.of(n % 10, Integer.toHexString(n)));
1041         final Multimap<Integer, String> dst = src.removeKeys(k -> k % 2 == 0);
1042         assertThat(dst).isEqualTo(emptyIntString().put(1, "1").put(1, "b").put(3, "3").put(3, "d").put(5, "5").put(5, "f").put(7, "7").put(7, "11").put(9, "9").put(9, "13"));
1043     }
1044 
1045     @Test
1046     public void shouldValueRemoveWork() throws Exception {
1047         final Multimap<Integer, String> src = mapTabulate(20, n -> Tuple.of(n % 10, Integer.toHexString(n)));
1048         final Pattern isDigits = Pattern.compile("^\\d+$");
1049         final Multimap<Integer, String> dst = src.removeValues(v -> isDigits.matcher(v).matches());
1050         assertThat(dst).isEqualTo(emptyIntString().put(0, "a").put(1, "b").put(2, "c").put(3, "d").put(4, "e").put(5, "f"));
1051     }
1052 
1053     // -- getOrElse
1054 
1055     @Test
1056     public void shouldReturnDefaultValue() {
1057         final Multimap<String, String> map = mapOf("1", "a").put("2", "b");
1058         assertThat(map.getOrElse("3", io.vavr.collection.List.of("3"))).isEqualTo(io.vavr.collection.List.of("3"));
1059     }
1060 
1061     // -- disabled super tests
1062 
1063     @Override
1064     @Test
1065     public void shouldCreateSeqOfSeqUsingCons() {
1066         // this Traversable test is not suited for Multimaps:
1067         //   io.vavr.collection.List$Nil cannot be cast to java.lang.Comparable
1068     }
1069 
1070     @Override
1071     @Test
1072     public void shouldConvertToJavaArrayWithTypeHintPrimitiveVoid() {
1073         // this Value test is not suited for Multimaps:
1074         //   java.lang.NullPointerException at io.vavr.collection.Comparators.lambda$naturalComparator
1075     }
1076 
1077     // -- spliterator
1078 
1079     @Test
1080     public void shouldHaveSizedSpliterator() {
1081         assertThat(of(1, 2, 3).spliterator().hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)).isTrue();
1082     }
1083 
1084     @Test
1085     public void shouldHaveDistinctSpliterator() {
1086         assertThat(of(1, 2, 3).spliterator().hasCharacteristics(Spliterator.DISTINCT)).isTrue();
1087     }
1088 
1089     @Test
1090     public void shouldReturnSizeWhenSpliterator() {
1091         assertThat(of(1, 2, 3).spliterator().getExactSizeIfKnown()).isEqualTo(3);
1092     }
1093 
1094     @Override
1095     @Test
1096     @SuppressWarnings("unchecked")
1097     public void shouldPartitionIntsInOddAndEvenHavingOddAndEvenNumbers() {
1098         assertThat(of(1, 2, 3, 4).partition(i -> i % 2 != 0))
1099                 .isEqualTo(Tuple.of(mapOfTuples(Tuple.of(0, 1), Tuple.of(2, 3)),
1100                         mapOfTuples(Tuple.of(1, 2), Tuple.of(3, 4))));
1101     }
1102 
1103     @Override
1104     @Test
1105     @SuppressWarnings("unchecked")
1106     public void shouldSpanNonNil() {
1107         assertThat(of(0, 1, 2, 3).span(i -> i < 2))
1108                 .isEqualTo(Tuple.of(mapOfTuples(Tuple.of(0, 0), Tuple.of(1, 1)),
1109                         mapOfTuples(Tuple.of(2, 2), Tuple.of(3, 3))));
1110     }
1111 
1112     @Override
1113     @Test
1114     @SuppressWarnings("unchecked")
1115     public void shouldSpanAndNotTruncate() {
1116         assertThat(of(1, 1, 2, 2, 3, 3).span(x -> x % 2 == 1))
1117                 .isEqualTo(Tuple.of(mapOfTuples(Tuple.of(0,1), Tuple.of(1, 1)),
1118                         mapOfTuples(Tuple.of(2, 2), Tuple.of(3, 2),
1119                                 Tuple.of(4, 3), Tuple.of(5, 3))));
1120         assertThat(of(1, 1, 2, 2, 4, 4).span(x -> x == 1))
1121                 .isEqualTo(Tuple.of(mapOfTuples(Tuple.of(0,1), Tuple.of(1, 1)),
1122                         mapOfTuples(Tuple.of(2, 2), Tuple.of(3, 2),
1123                                 Tuple.of(4, 4), Tuple.of(5, 4))));
1124     }
1125 
1126     @Override
1127     @Test
1128     public void shouldNonNilGroupByIdentity() {
1129         final Map<?, ?> actual = of('a', 'b', 'c').groupBy(Function.identity());
1130         final Map<?, ?> expected = LinkedHashMap.empty().put('a', mapOf(0, 'a')).put('b', mapOf(1,'b'))
1131                 .put('c', mapOf(2,'c'));
1132         assertThat(actual).isEqualTo(expected);
1133     }
1134 }